import Local from "@/utils/local";
import type { LoginSubmitResponsePermissionTreeDTOListType, LoginSubmitResponseUserType } from "@/types/loginType.ts";
import type { RouteRecordRaw } from "vue-router";
import router from "@/router";
import Layout from '@/layout/index.vue'
import {downloadFile3, downloadFile3Out} from "@/services/newsApi"

export const getBaseURL = () => import.meta.env.VITE_API_BASE_URL

export const getOutURL = (): string => {
    // console.log(import.meta.env)
    return import.meta.env.VITE_API_OUTER_URL
}

/**
 * 递归查询树形元素
 *
 * 通过递归的方式在树形结构中查找与目标 `id` 匹配的节点。
 *
 * @template T - 树中每个节点的类型
 * @template D - 查找的 `id` 的类型
 *
 * @param {T[]} treeData - 树形数据的数组
 * @param {D} id - 目标节点的标识符值
 * @param {keyof T} key - 用于匹配 `id` 的键（属性名）
 * @param {keyof T} children - 子节点数组的键（属性名）
 *
 * @returns {T | null} 返回匹配的节点。如果没有找到，则返回 `null`
 *
 * @example
 * const tree = [
 *   { id: 1, name: 'A', children: [
 *       { id: 2, name: 'B', children: [] },
 *       { id: 3, name: 'C', children: [
 *           { id: 4, name: 'D', children: [] },
 *       ] },
 *   ] },
 * ];
 *
 * const result = getTreeItem(tree, 4, 'id', 'children');
 * console.log(result); // 输出: { id: 4, name: 'D', children: [] }
 */
export const getTreeItem = <T, D>(
    treeData: T[],
    id: D,
    key: keyof T,
    children: keyof T
): T | null => {

    if (!Array.isArray(treeData) || treeData.length === 0) {
        return null;
    }

    for (const item of treeData) {

        if (item[key] === id) {
            return item;
        }

        const childNodes = item[children];
        if (Array.isArray(childNodes)) {
            const found = getTreeItem(childNodes as T[], id, key, children);
            if (found) {
                return found;
            }
        }
    }

    return null;
};

/**
 * 对手机号中间四位进行掩码处理
 *
 * 将手机号中间的四位数字替换为 `****`，返回掩码处理后的手机号。
 *
 * @param {string} phone - 需要进行掩码处理的手机号字符串
 *
 * @returns {string} 返回掩码处理后的手机号。如果输入为空或无效，则返回空字符串 `''`
 *
 * @example
 * const phone = '13812345678';
 * const masked = maskPhone(phone);
 * console.log(masked); // 输出: '138****5678'
 *
 * @example
 * const phone = '';
 * const masked = maskPhone(phone);
 * console.log(masked); // 输出: ''
 */
export const maskPhone = (phone: string): string => {
    if (!phone || !/^\d{11}$/.test(phone)) return '' // 校验手机号是否为 11 位数字

    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')

    // return phone
}

/**
 * WGS84 转 GCJ02（火星坐标系）
 *
 * 将 WGS84 坐标系下的经纬度转换为 GCJ02 坐标系（中国使用的火星坐标系）。
 * 如果输入坐标不在中国范围内，则直接返回原始坐标。
 *
 * @param {number} lng - WGS84 坐标系下的经度
 * @param {number} lat - WGS84 坐标系下的纬度
 *
 * @returns {[number, number]} 转换后的 GCJ02 坐标系经纬度，以 `[经度, 纬度]` 的形式返回
 *
 * @example
 * // 在中国范围内的坐标转换
 * const [gcjLng, gcjLat] = wgs84ToGcj02(116.404, 39.915);
 * console.log(gcjLng, gcjLat); // 输出类似: [116.41024449916938, 39.91640428150164]
 *
 * @example
 * // 不在中国范围的坐标，直接返回原始值
 * const [gcjLng, gcjLat] = wgs84ToGcj02(10.0, 20.0);
 * console.log(gcjLng, gcjLat); // 输出: [10.0, 20.0]
 */
export function wgs84ToGcj02(lng: number, lat: number): [number, number] {
    const pi = Math.PI

    // 判断是否在中国范围
    function isInChina(lng: number, lat: number): boolean {
        return !(lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271)
    }

    // 偏移算法
    function transformLat(x: number, y: number): number {
        let ret =
            -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
        ret += ((20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0) / 3.0
        ret += ((20.0 * Math.sin(y * pi) + 40.0 * Math.sin((y / 3.0) * pi)) * 2.0) / 3.0
        ret += ((160.0 * Math.sin((y / 12.0) * pi) + 320 * Math.sin((y * pi) / 30.0)) * 2.0) / 3.0
        return ret
    }

    function transformLng(x: number, y: number): number {
        let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
        ret += ((20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0) / 3.0
        ret += ((20.0 * Math.sin(x * pi) + 40.0 * Math.sin((x / 3.0) * pi)) * 2.0) / 3.0
        ret += ((150.0 * Math.sin((x / 12.0) * pi) + 300.0 * Math.sin((x / 30.0) * pi)) * 2.0) / 3.0
        return ret
    }

    if (!isInChina(lng, lat)) {
        return [lng, lat] // 不在中国范围，直接返回原坐标
    }

    const a = 6378245.0 // 地球长半轴
    const ee = 0.006693421622965943 // 偏心率平方
    const dLat = transformLat(lng - 105.0, lat - 35.0)
    const dLng = transformLng(lng - 105.0, lat - 35.0)
    const radLat = (lat / 180.0) * pi
    const magic = Math.sin(radLat)
    const magic2 = 1 - ee * magic * magic
    const sqrtMagic = Math.sqrt(magic2)
    const adjustedLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic2 * sqrtMagic)) * pi)
    const adjustedLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * pi)

    return [lng + adjustedLng, lat + adjustedLat]
}

/**
 * 格式化金额显示
 *
 * 将输入的数字或字符串形式的金额格式化为标准的带千分位逗号分隔的字符串，并保留两位小数。
 *
 * @param {string | number} mount - 输入的金额，可以是字符串或数字形式
 *
 * @returns {string} 格式化后的金额字符串，带千分位分隔符和两位小数。
 * 如果金额为 0，则返回 `'0'`。
 *
 * @example
 * // 数字形式输入
 * const formatted1 = formatAmount(1234567.8);
 * console.log(formatted1); // 输出: '1,234,567.80'
 *
 * @example
 * // 字符串形式输入
 * const formatted2 = formatAmount('9876543.2');
 * console.log(formatted2); // 输出: '9,876,543.20'
 *
 * @example
 * // 金额为 0
 * const formatted3 = formatAmount(0);
 * console.log(formatted3); // 输出: '0'
 */
export const formatAmount = (mount: string | number): string => {
    const amount = Number(mount)

    if (amount === 0) return '0' // 当 amount 为 0 时，直接返回数字 0

    const fixedAmount = amount.toFixed(2)

    const parts = fixedAmount.split('.')
    const integerPart = parts[0]
    const decimalPart = parts[1]

    const integerPartWithCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

    return `${integerPartWithCommas}.${decimalPart}`
}

/**
 * Formats a given date (Date object or string) into a specified format.
 * Supports both Chinese and English locales for weekdays.
 *
 * 格式化日期，将传入的日期对象或字符串转换为指定格式。
 * 支持中文和英文语言环境的星期格式。
 *
 * @param {Date | string} input - The input date as a Date object or string.
 *                                输入的日期对象或字符串。
 * @param {string} [format='YYYY-MM-DD HH:mm:ss'] - The format pattern for the output date.
 *                                                  Default is 'YYYY-MM-DD HH:mm:ss'.
 *                                                  日期的格式化模式，默认为 'YYYY-MM-DD HH:mm:ss'。
 * @param {'zh' | 'en'} [locale='zh'] - The locale for formatting weekdays:
 *                                      'zh' for Chinese and 'en' for English. Default is 'zh'.
 *                                      格式化星期时的语言：'zh' 表示中文，'en' 表示英文。默认为中文。
 *
 * @returns {string} The formatted date string according to the provided format and locale.
 *                   根据提供的格式和语言返回格式化日期字符串。
 *
 * @throws {Error} If the input date is invalid, an error will be thrown.
 *                 如果输入的日期无效，则会抛出错误。
 *
 * @example
 * // Format a date in default Chinese locale
 * const resultZh = formatData(new Date(), 'YYYY-MM-DD ddd HH:mm:ss', 'zh');
 * console.log(resultZh); // Outputs: "2023-10-25 星期三 15:45:30"
 *
 * @example
 * // Format a date in English locale with a custom format
 * const resultEn = formatData(new Date(), 'DD/MM/YYYY ddd HH:mm', 'en');
 * console.log(resultEn); // Outputs: "25/10/2023 Wednesday 15:45"
 *
 * @example
 * // Format a date with short weekday in English locale
 * const resultShortEn = formatData(new Date(), 'YYYY-MM-DD day', 'en');
 * console.log(resultShortEn); // Outputs: "2023-10-25 Wed"
 *
 * @example
 * // Throws error for invalid date input
 * try {
 *     formatData('invalid-date');
 * } catch (error) {
 *     console.error(error.message); // Outputs: "Invalid date input"
 * }
 */
export const formatData = (
    input: Date | string,
    format = 'YYYY-MM-DD HH:mm:ss',
    locale: 'zh' | 'en' = 'zh'
): string => {
    const date = input instanceof Date ? input : new Date(input);
    if (isNaN(date.getTime())) {
        throw new Error('Invalid date input');
    }

    const year = String(date.getFullYear());
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');

    // Weekdays in Chinese and English locales
    const fullWeekdaysZh = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
    const shortWeekdaysZh = ['日', '一', '二', '三', '四', '五', '六'];
    const fullWeekdaysEn = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    const shortWeekdaysEn = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

    const fullWeekday = locale === 'zh' ? fullWeekdaysZh[date.getDay()] : fullWeekdaysEn[date.getDay()];
    const shortWeekday = locale === 'zh' ? shortWeekdaysZh[date.getDay()] : shortWeekdaysEn[date.getDay()];

    return format
        .replace('YYYY', year)
        .replace('MM', month)
        .replace('DD', day)
        .replace('HH', hours)
        .replace('mm', minutes)
        .replace('ss', seconds)
        .replace('ddd', fullWeekday) // Full weekday name
        .replace('day', shortWeekday); // Short weekday name
};

// 获取token
export const getToken = () => Local.localRead('token')
// 获取用户信息
export const getUser = (): LoginSubmitResponseUserType => {
    const user = Local.localRead('user') || '{}'
    return JSON.parse(user)
}

export const getRoleCode = (): string => {
    return Local.localRead('roleCode') || ''
}

//绑定路由
export const buildRoutes = (menuRouteList: LoginSubmitResponsePermissionTreeDTOListType[]) => {
    const menuList = menuRouteList
    // 获取vue引用地址
    const modules = import.meta.glob('../pages/**/**.vue')
    // 获取vue所有组件
    const routerList: RouteRecordRaw[] = []
    /**
     * 递归处理菜单树，生成路由配置
     * @param list 当前菜单节点列表
     * @param arr 路由配置数组
     */
    const routerRecursion = (list: LoginSubmitResponsePermissionTreeDTOListType[], arr: RouteRecordRaw[]) => {
        for (const menuRoute of list) {
            if (menuRoute.menuType !== 2) {
                // 路由节点
                const route: RouteRecordRaw = {
                    path: menuRoute.url,
                    name: menuRoute.componentName,
                    meta: {
                        title: menuRoute.name,
                        hideInMenu: menuRoute.hidden, // 用于控制是否隐藏菜单
                        internalOrExternal: menuRoute.internalOrExternal, // 路由打开方式
                        renameComponentFlag: false,
                        isTab: menuRoute.hideTab
                    },
                    component: menuRoute.component === '/layout/index.vue' ? Layout : modules[`../pages${menuRoute.component.trim()}`] as any, // 动态加载组件
                    children: []
                };
                if (menuRoute.childrenType === 1) {
                    if (menuRoute.children && menuRoute.children.length > 0) {
                        routerRecursion(menuRoute.children, route.children as RouteRecordRaw[])
                    }
                }
                arr.push(route)
            }
        }
    }
    routerRecursion(menuList, routerList)
    for (const menuRoute of routerList) {
        router.addRoute(menuRoute)
    }
}


export const downloadFile = (blob: Blob, fileName: string) => {
    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = fileName
    link.click()
    URL.revokeObjectURL(url)
    link.remove()
}

export const windowURL = () => {
    return getBaseURL().slice(0, -1);
}

//  头像
export const getAvatar = (url: string) => {
    const baseURL = windowURL();
    return baseURL + url;
}



/**
 * 转换十六进制颜色值为 RGB 格式的字符串。
 * 支持 3 位简写格式（如 `#RGB`）和 6 位格式（如 `#RRGGBB`）。
 *
 * @param {string} hex - 十六进制颜色字符串，例如 `#fff` 或 `#ffffff`
 * @returns {string} - 返回 RGB 格式的字符串，例如 `255, 255, 255`
 *
 * @example
 * hexToRgb("#fff"); // 返回 "255, 255, 255"
 * hexToRgb("#ffffff"); // 返回 "255, 255, 255"
 * hexToRgb("#f00"); // 返回 "255, 0, 0"
 */
export const hexToRgb = (hex: string): string => {
    if (hex) {
        // 检查是否为缩写形式#RGB
        if (hex.length === 4) {
            // 将#RGB 转为 #RRGGBB
            hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
        }

        // 去掉#，方便处理
        hex = hex.replace("#", "");

        const r = parseInt(hex.substring(0, 2), 16); // 提取红色部分
        const g = parseInt(hex.substring(2, 4), 16); // 提取绿色部分
        const b = parseInt(hex.substring(4, 6), 16); // 提取蓝色部分

        return `${r}, ${g}, ${b}`;
    }
    return '12, 145, 233'
}
/**
 * 将十六进制颜色值转换为 RGB 对象
 * @param hex 十六进制颜色字符串，例如 "#ffffff" 或 "#fff"
 * @returns {{ r: number, g: number, b: number }} - 返回 RGB 对象
 */
export const hexToRgbObject = (hex: string): { r: number; g: number; b: number } => {
    if (hex.length === 4) {
        hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
    }

    const result: RegExpMatchArray | null = hex.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);

    if (result) {
        return {
            r: parseInt(result![1], 16),
            g: parseInt(result![2], 16),
            b: parseInt(result![3], 16),
        };
    }
    return {
        r: 255,
        g: 255,
        b: 255
    }
};

/**
 * 判断 RGB 值是否为白色系
 * @param r 红色通道值 (0-255)
 * @param g 绿色通道值 (0-255)
 * @param b 蓝色通道值 (0-255)
 * @param threshold 阈值 (默认 220)
 * @returns {boolean} - 如果是白色系，返回 true
 */
export const isWhiteColor = (r: number, g: number, b: number, threshold = 220): boolean => {
    // 判断 r, g, b 是否都高于指定阈值
    return r >= threshold && g >= threshold && b >= threshold;
};

/**
 * 判断十六进制颜色是否为白色系
 * @param hex 十六进制颜色字符串，例如 "#ffffff"
 * @param threshold 可调节的阈值 (默认 220)
 * @returns {boolean} - 如果是白色系，返回 true
 */
export const isWhiteHex = (hex: string, threshold = 220): boolean => {
    const { r, g, b } = hexToRgbObject(hex); // 提取 RGB 值
    return isWhiteColor(r, g, b, threshold); // 根据 RGB 判断
};



export const download = (path: string, name: string) => {
    downloadFile3(path).then((res) => {
        const blob = res as Blob
        const downloadUrl = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = downloadUrl;
        link.download = name
        link.click(); // 触发下载
        window.URL.revokeObjectURL(downloadUrl);
    })
}


export const downloadOut = (path: string, name: string) => {
    downloadFile3Out(path).then((res) => {
        const blob = res as Blob
        const downloadUrl = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = downloadUrl;
        link.download = name
        link.click(); // 触发下载
        window.URL.revokeObjectURL(downloadUrl);
    })
}